package uk.co.q3c.deplan.server.rcp.model;

import java.util.ArrayList;
import java.util.List;

import uk.co.q3c.deplan.client.domain.dag.ChangeInvalidException;
import uk.co.q3c.deplan.client.domain.dag.CycleDetectedException;
import uk.co.q3c.deplan.client.domain.resource.PersistenceReferenceException;
import uk.co.q3c.deplan.client.domain.task.BaseTask;
import uk.co.q3c.deplan.client.domain.task.Task;
import uk.co.q3c.deplan.client.service.TaskService;
import uk.co.q3c.deplan.server.Server;
import uk.co.q3c.deplan.server.dao.Model;

/**
 * 
 * This class fulfils manages changes to the structure of tasks, for example moving tasks up and down the hierarchy. <br>
 * <br>
 * Used by {@link TaskGanttView} <br>
 * Used by {@link TaskTableView}
 * 
 * Monitoring of property changes moved to the {@link UndoRestoreList}
 * 
 * @author DSowerby 13 Apr 2009
 * @author DSowerby 30 Jun 2008 22:21:29
 * 
 */
public class TaskManager {

	private static final long serialVersionUID = 1L;
	private final List<TaskListListener> listeners = new ArrayList<TaskListListener>();

	private BaseTask root;

	private int saveCount;

	TaskService taskDao = Server.connection()
			.taskService();

	public TaskManager() {

	}

	// // TaskList Listener Methods /////
	public void addTaskListListener(TaskListListener listener) {

		if (!listeners.contains(listener)) listeners.add(listener);
	}

	/**
	 * Creates a new task with the supplied <code>parentTask</code>. When called <code>parentTask</code> may not be a summary task, and if
	 * that's the case it will be converted to one in order to attach the new task to it.
	 * 
	 * 
	 * if parentTask is null the new task will have no parent
	 * 
	 * no explicit call to save this is needed because setting the id causes its property to be fired, and task manager then automatically
	 * stores it as a changed object
	 * 
	 * If a summary task is created (to replace an atomic task passed in <code>parentTask</code> the original <code>parentTask</code> is
	 * deleted from the database. Any predecessors or successors it may have had are purged and therefore lost
	 * 
	 * @param parentTask
	 * @throws CycleDetectedException
	 * @throws ChangeInvalidException
	 * @throws PersistenceReferenceException
	 */
	public Task createTask(Task parentTask) throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		// BaseTask st;
		// BaseTask std = new BaseTask(Model.getConnection());
		//
		// if (parentTask == null) {
		// return std;
		// }
		// // if parent already a summary use it
		// if (parentTask instanceof ProjectAbstractTask) {
		// st = (BaseTask) parentTask;
		// } else {
		// // otherwise convert it to one
		// st = convertToSummary(parentTask);
		//
		// }
		// std.setParentTask(st);
		// return std;
		throw new RuntimeException("not yet implemented");
	}

	// useful call for temp files
	// return
	// DePlanRCPPlugin.getDefault().getStateLocation().append("contacts.xml").
	// toFile
	// ();

	/**
	 * Uses taskDao to discard this task (see {@link TaskService#discard(Task)}); this also calls {@link Task#dereference()} to remove any
	 * references to the <code>selectedTask</code> from parent, successors predecessors etc.
	 * 
	 * @param selectedTask
	 * @throws CycleDetectedException
	 * @throws PersistenceReferenceException
	 */
	public void discardTask(Task selectedTask) throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		Server.connection()
				.taskService()
				.discard(selectedTask);
	}

	/**
	 * Demotes the given task to a level down the hierarchy. It takes the present sibling above it in order as its new parent. If not such
	 * sibling exists no change is made.
	 * 
	 * @param st11
	 * @throws CycleDetectedException
	 * @throws ChangeInvalidException
	 * @throws PersistenceReferenceException
	 */
	public void demote(Task task) throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		int presentIndex = task.getParentTask()
				.indexOf(task);
		if (presentIndex == 0) {
			return;
		}

		Task prevSibling = task.getParentTask()
				.getSubTasks()
				.get(presentIndex - 1);
		BaseTask newParent;

		newParent = (BaseTask) prevSibling;

		task.setParentTask(newParent);
	}

	public BaseTask getRoot() {
		return root;
	}

	/**
	 * Total number of save actions since this task manager instantiated.
	 * 
	 * @return
	 */
	public int getSaveCount() {
		return saveCount;
	}

	/**
	 * Creates a project whose parent is the root of the hierarchy.
	 * 
	 * @throws CycleDetectedException
	 * @throws ChangeInvalidException
	 * @throws PersistenceReferenceException
	 * 
	 */
	public BaseTask newTopLevelProject() throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		BaseTask project = new BaseTask(Model.getConnection());
		project.setParentTask(root);
		project.setName("new project");
		project.setProject();
		return project;
	}

	/**
	 * Moves <code>selectedTask</code> down one in its parent's subTasks order. Ignored if parent is null or <code>selectedTask</code> is
	 * already at the bottom of the list
	 * 
	 * @see #orderUp(Task)
	 * 
	 * @param selectedTask
	 * @throws PersistenceReferenceException
	 */
	public void orderDown(Task selectedTask) throws PersistenceReferenceException {
		BaseTask st = selectedTask.getParentTask();
		if (st == null) {
			return;
		}
		st.orderDown(selectedTask);

	}

	/**
	 * Moves <code>selectedTask</code> up one in its parent's subTasks order. Ignored if parent is null or <code>selectedTask</code> is
	 * already at the top of the list
	 * 
	 * @see #orderDown(Task)
	 * 
	 * @param selectedTask
	 * @throws PersistenceReferenceException
	 */
	public void orderUp(Task selectedTask) throws PersistenceReferenceException {
		BaseTask st = selectedTask.getParentTask();
		if (st == null) {
			return;
		}
		st.orderUp(selectedTask);
	}

	/**
	 * Promotes the given task up a level in the tree hierarchy. Call is ignored if there isn't a level to promote it to. Task will only be
	 * promoted to a level where it still has a parent task. A parent task value of null will not be assigned.
	 * 
	 * @param task
	 * @throws CycleDetectedException
	 * @throws ChangeInvalidException
	 * @throws PersistenceReferenceException
	 */
	public void promote(Task task) throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		BaseTask parent = task.getParentTask();
		if (parent == null) {
			return;
		}
		BaseTask grandParent = parent.getParentTask();
		if (grandParent == null) {
			return;
		}
		task.setParentTask(grandParent);
	}

	public void removeTaskListListener(TaskListListener listener) {
		listeners.remove(listener);
	}

	// public void resourceChanged(IResourceChangeEvent event) {
	// // TODO Auto-generated method stub
	//
	// }

	public void setRoot(BaseTask root) {
		this.root = root;
	}

	public int taskCount() throws PersistenceReferenceException {
		if (root == null) {
			return 0;
		} else {
			return root.taskCount();
		}

	}

}
